package cn.buk.tms.dao;

import cn.buk.common.sc.CommonSearchCriteria;
import cn.buk.common.sc.Page;
import cn.buk.tms.entity.bill.BillFlightRefundTicket_;
import cn.buk.tms.entity.misc.*;
import cn.buk.tms.sc.SearchQinfoCriteria;
import cn.buk.tms.sc.SearchTprCriteria;
import cn.buk.common.util.DateUtil;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;

import cn.buk.tms.entity.bill.BillFlightRefundTicket;
import cn.buk.tms.entity.bill.BillFlightTicket;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.springframework.stereotype.Component;

/**
 * Created by yfdai on 16/6/13.
 */
@Transactional
@Component
public class FlightCharterDaoImpl extends AbstractDao implements FlightCharterDao {

    private static final Logger logger = LogManager.getLogger(FlightCharterDaoImpl.class);

    /**
     * 查找Q信息
     */
    @Override
    public List<Qinfo> searchQinfo(final int enterpriseId, SearchQinfoCriteria sc) {
        List<Qinfo> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Qinfo> cq = cb.createQuery(Qinfo.class);

        Root<Qinfo> root = cq.from(Qinfo.class);
        root.alias("o");

        var predicates0 = getPredicates_searchQinfo(enterpriseId, sc, cb, root);


        // count items
        CriteriaQuery<Long> cq0 = cb.createQuery(Long.class);
        Root<Qinfo> root0 = cq0.from(Qinfo.class);
        root0.alias("o");

        var predicates1 = getPredicates_searchQinfo(enterpriseId, sc, cb, root0);

        cq0.select(cb.count(root0)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(cq0).getSingleResult();

        sc.getPage().setRowCount(count.intValue());


        int firstPosition = (sc.getPage().getPageNo() - 1) * sc.getPage().getPageSize();
        cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.desc(root.get("id")));
        cq.orderBy(orderByes);

        results = em.createQuery(cq).
                setFirstResult(firstPosition).
                setMaxResults(sc.getPage().getPageSize())
                .getResultList();

        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    private static List<Predicate> getPredicates_searchQinfo(int enterpriseId, SearchQinfoCriteria sc, CriteriaBuilder cb, Root<Qinfo> root) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.equal(root.get(Qinfo_.enterpriseId), enterpriseId);
        predicates.add(predicate);

        if (sc.getQtype().length() == 2) {
            predicate = cb.like(root.get(Qinfo_.qtype), sc.getQtype());
            predicates.add(predicate);
        }

        if (sc.getPnrNo().length() > 0) {
            predicate = cb.like(root.get(Qinfo_.pnrno), "%" + sc.getPnrNo() + "%");
            predicates.add(predicate);
        }

        if (sc.getUsername().length() > 0) {
            predicate = cb.like(root.get(Qinfo_.username), "%" + sc.getUsername() + "%");
            predicates.add(predicate);
        }
        return predicates;
    }

    @Override
    public int reInformQinfo(int enterpriseId, int id) {
        return em.createQuery("update Qinfo o set o.beManaged = '0' where o.enterpriseId = :enterpriseId and o.id = :id ")
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("id", id)
                .executeUpdate();
    }

    @Override
    public int createQinfoNotice(QinfoNotice qinfoNotice) {
        if (qinfoNotice.getId() > 0) {
            qinfoNotice.setLastupdate(DateUtil.getCurDateTime());
            em.merge(qinfoNotice);
        } else {
            em.persist(qinfoNotice);
        }

        em.flush();


        return qinfoNotice.getId();
    }

    @Override
    public int deleteQinfoNotice(int enterpriseId, int id) {
        return em.createQuery("delete from QinfoNotice o where o.enterpriseId = :enterpriseId and o.id = :id")
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("id", id)
                .executeUpdate();
    }

    @Override
    public List<QinfoNotice> searchQinfoNotice(final int enterpriseId, CommonSearchCriteria sc) {
        List<QinfoNotice> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<QinfoNotice> cq = cb.createQuery(QinfoNotice.class);

        Root<QinfoNotice> root = cq.from(QinfoNotice.class);
        root.alias("o");

        var predicates0 = getPredicates_searchQinfo(enterpriseId, sc, cb, root);

        // count items
        CriteriaQuery<Long> cq0 = cb.createQuery(Long.class);
        Root<QinfoNotice> root0 = cq0.from(QinfoNotice.class);
        root0.alias("o");

        var predicates1 = getPredicates_searchQinfo(enterpriseId, sc, cb, root0);

        cq0.select(cb.count(root0)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(cq0).getSingleResult();

        sc.getPage().setRowCount(count.intValue());


        int firstPosition = (sc.getPage().getPageNo() - 1) * sc.getPage().getPageSize();
        cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.asc(root.get("conditionType")));
        orderByes.add(cb.asc(root.get("conditionValue")));
        cq.orderBy(orderByes);


        results = em.createQuery(cq)
                .setFirstResult(firstPosition)
                .setMaxResults(sc.getPage().getPageSize())
                .getResultList();


        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    private static List<Predicate> getPredicates_searchQinfo(int enterpriseId, CommonSearchCriteria sc, CriteriaBuilder cb, Root<QinfoNotice> root) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.conjunction();
        predicates.add(predicate);

        predicate = cb.equal(root.get(QinfoNotice_.enterpriseId), enterpriseId);
        predicates.add(predicate);

        if (sc.getName() != null && sc.getName().trim().length() > 0) {
            predicate = cb.like(root.get(QinfoNotice_.conditionValue), "%" + sc.getName().trim() + "%");
            predicates.add(predicate);
        }
        return predicates;
    }

    @Override
    public int saveSubclassInfo(SubclassInfo info) {
        if (info.getId() > 0) {
            info.setLastUpdate(DateUtil.getCurDateTime());
            em.merge(info);
        } else {
            em.persist(info);
        }

        return info.getId();
    }


    @Override
    public List<SubclassInfo> searchSubclassInfos(int enterpriseId, CommonSearchCriteria sc) {
        List<SubclassInfo> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<SubclassInfo> cq = cb.createQuery(SubclassInfo.class);
        Root<SubclassInfo> root = cq.from(SubclassInfo.class);
        root.alias("h");

        var predicates0 = getPredicates_searchSubclassInfos(enterpriseId, sc, cb, root);

        //计算根据条件查询得出的数据总数
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<SubclassInfo> countRoot = countQuery.from(SubclassInfo.class);
        countRoot.alias("h");

        var predicates1 = getPredicates_searchSubclassInfos(enterpriseId, sc, cb, countRoot);

        // count items
        countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(countQuery).getSingleResult();

        //查询
        cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.asc(root.get("psgType")));
        orderByes.add(cb.desc(root.get("discount")));
        orderByes.add(cb.asc(root.get("id")));

        cq.orderBy(orderByes);


        int maxResults = count.intValue();
        if (maxResults > 0) {
            Page page = sc.getPage();
            page.setRowCount(maxResults);
            int i = (page.getPageNo() - 1) * page.getPageSize();

            if (i < 0) {
              i = 0;
            }


            results = em.createQuery(cq)
                    .setFirstResult(i)
                    .setMaxResults(page.getPageSize()).getResultList();

        }

        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    private static List<Predicate> getPredicates_searchSubclassInfos(int enterpriseId, CommonSearchCriteria sc, CriteriaBuilder cb, Root<SubclassInfo> root) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.conjunction();
        predicates.add(predicate);

        predicate = cb.equal(root.get(SubclassInfo_.enterpriseId), enterpriseId);
        predicates.add(predicate);

        if (sc.getCarrier().length() == 2) {
            predicate = cb.equal(root.get(SubclassInfo_.carrier), sc.getCarrier());
            predicates.add(predicate);
        }

        if (sc.getSubclass().length() == 1) {
            predicate = cb.like(root.get(SubclassInfo_.subclass), "%" + sc.getSubclass() + "%");
            predicates.add(predicate);
        }
        return predicates;
    }


    @Override
    public BillFlightTicket searchBillFlightTicket(int enterpriseId, String balCode, String tickNo) {
        List<BillFlightTicket> tickets = em.createQuery("select o from BillFlightTicket o where " +
                "o.enterpriseId = :enterpriseId and o.balanceCode = :balCode and o.ticketNo = :tickNo", BillFlightTicket.class)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("balCode", balCode)
                .setParameter("tickNo", tickNo)
                .getResultList();

        return tickets.size() == 1 ? tickets.get(0) : null;
    }

    @Override
    public int createFlightRefundTicket(BillFlightRefundTicket refundTicket) {
        em.persist(refundTicket);
        return 1;
    }

    @Override
    public List<BillFlightRefundTicket> searchFlightRefundTickets(int enterpriseId, CommonSearchCriteria sc) {
        List<BillFlightRefundTicket> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<BillFlightRefundTicket> cq = cb.createQuery(BillFlightRefundTicket.class);
        Root<BillFlightRefundTicket> root = cq.from(BillFlightRefundTicket.class);
        root.alias("o");

        var predicates0 = getPredicates_searchFlightRefundTickets(enterpriseId, cb, root);

        cq.where(predicates0.toArray(new Predicate[predicates0.size()]));
        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.desc(root.get("id")));

        cq.orderBy(orderByes);

        //计算根据条件查询得出的数据总数
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<BillFlightRefundTicket> countRoot = countQuery.from(BillFlightRefundTicket.class);
        countRoot.alias("o");

        var predicates1 = getPredicates_searchFlightRefundTickets(enterpriseId, cb, countRoot);

        countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(countQuery).getSingleResult();

        int maxResults = count.intValue();
        if (maxResults > 0) {
            Page page = sc.getPage();
            page.setRowCount(maxResults);
            int i = (page.getPageNo() - 1) * page.getPageSize();

            if (i < 0) {
              i = 0;
            }

            results = em.createQuery(cq)
                    .setFirstResult(i)
                    .setMaxResults(page.getPageSize()).getResultList();
        }

        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    private static List<Predicate> getPredicates_searchFlightRefundTickets(int enterpriseId, CriteriaBuilder cb, Root<BillFlightRefundTicket> root) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate where = cb.conjunction();
        predicates.add(where);

        where = cb.equal(root.get(BillFlightRefundTicket_.enterpriseId), enterpriseId);
        predicates.add(where);
        return predicates;
    }

    /**
     * 忽略保存失败的
     */
    @Override
    public int createTicketSales(List<TicketSale> tickets) {
        int retCount = 0;
        for (TicketSale newTicket : tickets) {
            List<TicketSale> lists = em.createQuery("select o from TicketSale o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.ticketNo = :ticketNo " +
                    "and o.ticketStatus = :ticketStatus", TicketSale.class)
                    .setParameter("enterpriseId", newTicket.getEnterpriseId())
                    .setParameter("ticketNo", newTicket.getTicketNo())
                    .setParameter("ticketStatus", newTicket.getTicketStatus())
                    .getResultList();
            if (!lists.isEmpty()) {
                TicketSale oldTicket = lists.get(0);
                if (newTicket.getType3() != -1 && oldTicket.getType3() == -1) {
                    //检查已存在的票号的国际国内属性是否已赋值
                    oldTicket.setType3(newTicket.getType3());
                    oldTicket.setLastupdate(DateUtil.getCurDateTime());
                    em.merge(oldTicket);
                }
                continue;
            }

            em.persist(newTicket);
            retCount++;
        }

        return retCount;
    }

    @Override
    public List<TicketSale> searchTprs(int enterpriseId, Date sellDate) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<TicketSale> cq = cb.createQuery(TicketSale.class);
        Root<TicketSale> root = cq.from(TicketSale.class);
        root.alias("o");

        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.conjunction();
        predicates.add(predicate);

        predicate = cb.equal(root.get(TicketSale_.enterpriseId), enterpriseId);
        predicates.add(predicate);


        predicate = cb.equal(root.get(TicketSale_.sellDate), sellDate);
        predicates.add(predicate);


        //查询
        cq.select(root).where(predicates.toArray(new Predicate[predicates.size()]));

        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.asc(root.get("deviceId")));
        orderByes.add(cb.asc(root.get("ticketNo")));

        cq.orderBy(orderByes);

        return em.createQuery(cq).getResultList();
    }

    @Override
    public List<TicketSale> searchTprs(int enterpriseId, String ticketNo, int ticketStatus) {
        return searchTicketSale(enterpriseId, ticketNo, ticketStatus);
    }

    private List<Predicate> getPredicates_searchTprs(int enterpriseId, String ticketNo, SearchTprCriteria sc, CriteriaBuilder cb, Root<TicketSale> root) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.conjunction();
        predicates.add(predicate);

        predicate = cb.equal(root.get(TicketSale_.enterpriseId), enterpriseId);
        predicates.add(predicate);

        final Date beginDate = sc.getBeginDate();
        final Date endDate = sc.getEndDate();

        if (beginDate != null && endDate == null) {
            predicate = cb.greaterThanOrEqualTo(root.get(TicketSale_.sellDate), beginDate);
            predicates.add(predicate);
        } else if (beginDate == null && endDate != null) {
            predicate = cb.equal(root.get(TicketSale_.sellDate), endDate);
            predicates.add(predicate);
        } else if (beginDate != null && endDate != null) {
            predicate = cb.greaterThanOrEqualTo(root.get(TicketSale_.sellDate), beginDate);
            predicates.add(predicate);

            predicate = cb.lessThanOrEqualTo(root.get(TicketSale_.sellDate), endDate);
            predicates.add(predicate);
        }

        final int deviceId = sc.getDeviceId();
        if (deviceId > 0) {
            predicate = cb.equal(root.get(TicketSale_.deviceId), deviceId);
            predicates.add(predicate);
        }

        if (ticketNo != null && ticketNo.trim().length() > 0) {
            predicate = cb.like(root.get(TicketSale_.ticketNo), "%" + ticketNo.trim() + "%");
            predicates.add(predicate);
        }

        final int ticketStatus = Integer.parseInt(sc.getTicketStatus());
        if (ticketStatus > -1) {
            if (ticketStatus == 0) {
                //改签也包含在正常票里
                predicate = cb.equal(root.get(TicketSale_.ticketStatus), 0);
                predicate = cb.or(predicate, cb.equal(root.get(TicketSale_.ticketStatus), 1));
            } else {
                predicate = cb.equal(root.get(TicketSale_.ticketStatus), ticketStatus);
            }
            predicates.add(predicate);
        }

        final int checkStatus = sc.getCheckStatus();
        if (checkStatus > -1) {
            predicate = cb.equal(root.get(TicketSale_.checkStatus), checkStatus);
            predicates.add(predicate);
        }

        final int type3 = sc.getType3();
        if (type3 > -1) {
            predicate = cb.equal(root.get(TicketSale_.type3), type3);
            predicates.add(predicate);
        }

        return predicates;
    }

    @Override
    public List<TicketSale> searchTprs(int enterpriseId, String ticketNo, SearchTprCriteria sc) {
        List<TicketSale> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<TicketSale> cq = cb.createQuery(TicketSale.class);
        Root<TicketSale> root = cq.from(TicketSale.class);
        root.alias("o");

        var predicates0 = getPredicates_searchTprs(enterpriseId, ticketNo, sc, cb, root);

        //计算根据条件查询得出的数据总数
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<TicketSale> countRoot = countQuery.from(TicketSale.class);
        countRoot.alias("o");

        var predicates1 = getPredicates_searchTprs(enterpriseId, ticketNo, sc, cb, countRoot);

        countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(countQuery).getSingleResult();

        //查询
        cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

        List<Order> orderByes = new ArrayList<>();
        if (sc.getBeginDate() == null && sc.getEndDate() == null) {
            orderByes.add(cb.desc(root.get("id")));
        } else {
            orderByes.add(cb.asc(root.get("sellDate")));
            orderByes.add(cb.asc(root.get("deviceId")));
            orderByes.add(cb.asc(root.get("ticketNo")));
        }

        cq.orderBy(orderByes);

        int maxResults = count.intValue();
        if (maxResults > 0) {
            Page page = sc.getPage();
            page.setRowCount(maxResults);
            int i = (page.getPageNo() - 1) * page.getPageSize();

            if (i < 0) {
              i = 0;
            }

            results = em.createQuery(cq)
                    .setFirstResult(i)
                    .setMaxResults(page.getPageSize()).getResultList();
        }

        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    @Override
    public TicketSale getTicketSaleByTicketNo(int enterpriseId, String ticketNo, int ticketStatus) {
        List<TicketSale> list = em.createQuery("select o from TicketSale o " +
                "where o.enterpriseId = :enterpriseId " +
                "and o.ticketNo = :ticketNo " +
                "and o.ticketStatus = :ticketStatus", TicketSale.class)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("ticketNo", ticketNo)
                .setParameter("ticketStatus", ticketStatus)
                .getResultList();

        return list.size() > 0 ? list.get(0) : null;
    }


  @Override
  public SubclassInfo getSubclassInfoById(int id) {
      SubclassInfo info = em.find(SubclassInfo.class, id);
      if (info != null) {
          em.refresh(info);
      }
      return info;
  }

}
